package com.clp.protocol.iec104.iec104.server;

import com.clp.protocol.core.server.NettyServer;
import com.clp.protocol.iec104.iec104.server.slave.InSlave;
import com.clp.protocol.iec104.iec104.server.slave.Slave;
import com.clp.protocol.iec104.iec104.server.slave.SlaveConfig;
import com.clp.protocol.iec104.iec104.server.slave.SlaveContainer;
import com.clp.protocol.iec104.iec104.server.slave.async.SlaveFuture;
import com.clp.protocol.iec104.iec104.server.slave.async.SlaveFutureListener;

import java.io.Closeable;
import java.io.IOException;

public class Iec104Server implements Closeable {
    private static volatile Iec104Server server;

    public static Iec104Server get() {
        // 懒加载
        if (server != null && !server.isClosed()) {
            return server;
        }
        synchronized (Iec104Server.class) {
            if (server != null && !server.isClosed()) {
                return server;
            }
            server = new Iec104Server();
        }
        return server;
    }

    private final NettyServer nettyServer = new NettyServer(); // 内嵌服务端
    private final SlaveContainer container; // 子站容器

    private Iec104Server() {
        this.container = new SlaveContainer(nettyServer.scheduledExecutorService());
    }

    public synchronized boolean isClosed() {
        return nettyServer.isClosed();
    }

    public synchronized SlaveFuture<Void> open(SlaveConfig cfg) throws Throwable {
        checkNotClosed();

        // 检查配置
        cfg.check();
        // 检查 从站ip+端口号是否已经打开
        String localHost = cfg.getLocalHost();
        int localPort = cfg.getLocalPort();
        if (hasSlave(localHost, localPort)) {
            throw new PortAlreadyBindException(localHost, localPort);
        }

        InSlave slave = createSlave(cfg);
        try {
            SlaveFuture<Void> openFuture = slave.open();
            openFuture.addListener(new SlaveFutureListener<Void>() {
                @Override
                public void operationComplete(SlaveFuture<Void> future) {
                    if (future.isSuccess()) {
                        container.add(future.slave());
                    }
                }
            });
            return openFuture;
        } catch (Throwable ex) {
            return slave.newPromise(Void.class).setFailure(ex.getCause());
        }
    }

    private void checkNotClosed() {
        if (isClosed()) {
            throw new RuntimeException("客户端已关闭！");
        }
    }

    public boolean hasSlave(String localHost, int localPort) {
        return container.contains(localHost, localPort);
    }

    private InSlave createSlave(SlaveConfig cfg) {
        InSlave slave = new InSlave(nettyServer, cfg);
        return slave;
    }

    public synchronized SlaveFuture<Void> close(Slave slave) {
        checkNotClosed();
        try {
            SlaveFuture<Void> closeFuture = ((InSlave) slave).close();
            closeFuture.addListener(new SlaveFutureListener<Void>() {
                @Override
                public void operationComplete(SlaveFuture<Void> future) {
                    if (future.isSuccess()) {
                        container.remove(future.slave());
                    }
                }
            });
            return closeFuture;
        } catch (Throwable ex) {
            return slave.newPromise(Void.class).setFailure(ex);
        }
    }

    @Override
    public synchronized void close() throws IOException {
        if (isClosed()) return;
        server.close();
    }
}
